<?php

namespace hiduoke\services\upload\driver;


use hiduoke\base\BaseUpload;
use hiduoke\exception\UploadException;
use think\facade\Filesystem;
use think\File;

/**
 * 本地上传 Class Local
 * @package hiduoke\services\upload\driver
 * User: liuhongbin
 * Date: 2021-10-03 18:24
 */
class Local extends BaseUpload
{

    /**
     * 默认存放路径
     * @var string
     */
    protected $defaultPath;
    protected $storageDomain;

    public function initialize(array $config)
    {
        parent::initialize($config);
        $this->storageDomain = get_host() . config('filesystem.disks.public.url');
        $this->defaultPath = "";
    }

    protected function app()
    {
        return Filesystem::disk('public');
    }

    /**
     * 生成上传文件目录
     * @param $path
     * @param null $root
     * @return string
     */
    protected function uploadDir($path, $root = null)
    {
        if ($root === null) $root = app()->getRootPath() . 'public/';
        return str_replace('\\', '/', $root . 'uploads/' . $path);
    }

    /**
     * 检查上传目录不存在则生成
     * @param $dir
     * @return bool
     */
    protected function validDir($dir)
    {
        return is_dir($dir) == true || mkdir($dir, 0777, true) == true;
    }

    /**
     * 文件上传
     * @param string|file\UploadedFile|null $file 附件名
     * @param string|null $Stream 流内容
     * @param bool $isStream 是否为流上传
     * @return array|bool|mixed|\StdClass
     */
    public function upload($file = null, $Stream = null, $isStream = false)
    {
        // 流上传处理
        if ($isStream) {
            return $this->handleStreamUpload($Stream, $file);
        }

        // 文件上传处理（支持多种输入类型）
        $fileHandle = null;
        $fileName = null;

        if(is_string($file)){
            // 1. 字符串参数 - 从请求中获取文件
            $fileHandle = app()->request->file($file);
            $fileName = $file;
        } elseif($file instanceof \think\file\UploadedFile) {
            // 2. UploadedFile 对象 - 直接使用
            $fileHandle = $file;
            $fileName = $file->getFilename();
        } elseif(is_resource($file)) {
            // 3. 资源流 - 转换为流上传
            $streamContent = stream_get_contents($file);
            $key = $Stream ?: $this->createFileName();
            return $this->handleStreamUpload($streamContent, $key);
        } else {
            // 4. 其他情况 - 尝试作为文件路径处理
            if ($file && file_exists($file)) {
                $fileName = basename($file);
                $mimeType = mime_content_type($file) ?: 'application/octet-stream';
                $fileHandle = new \think\file\UploadedFile($file, $fileName, $mimeType, 0);
            }
        }
        if (!$fileHandle) {
            throw new UploadException("上传配置有误");
        }
        $this->validateCheck($fileName, $fileHandle);

        //这里得到的是相对路径
        $filePath = $this->app()->putFile($this->path, $fileHandle);
        if (!$filePath) {
            throw new UploadException('上传失败');
        }
        //这里得到的是完整路径
        $_filePath = $this->app()->path($filePath);
        $this->fileInfo->uploadInfo = new File($_filePath);
        $this->fileInfo->filePath = $filePath;
        $this->fileInfo->fileUrl = $this->storageDomain . '/' . $filePath;
        $this->fileInfo->oldName = $fileHandle->getOriginalName();
        $this->fileInfo->fileName = $this->fileInfo->uploadInfo->getFilename();
        return $this->fileInfo;
    }

    /**
     * 处理流上传
     * @param string $Stream 流内容
     * @param string|null $key 文件名
     * @return \StdClass
     */
    protected function handleStreamUpload($Stream, $key = null)
    {
        if (!$key) {
            $key = $this->createFileName();
        }

        // 流内容验证
        $this->validateStreamContent($Stream, $key);

        $dirPath = $this->defaultPath . '/' . $this->path;
        if (!is_dir($dirPath)) {
            if (!mkdir($dirPath, 0755, true)) {
                throw new UploadException('无法创建目录');
            }
        }

        $filePath = $this->path . '/' . $key;
        $fullPath = $this->defaultPath . '/' . $filePath;

        if (file_put_contents($fullPath, $Stream) === false) {
            throw new UploadException('流上传失败');
        }

        $this->fileInfo->uploadInfo = new File($fullPath);
        $this->fileInfo->filePath = $filePath;
        $this->fileInfo->fileUrl = $this->storageDomain . '/' . $filePath;
        $this->fileInfo->oldName = $key;
        $this->fileInfo->realName = $key;
        $this->fileInfo->fileName = $key;
        return $this->fileInfo;
    }

    /**
     * 验证流内容
     * @param string $Stream 流内容
     * @param string $key 文件名
     */
    protected function validateStreamContent($Stream, $key)
    {
        if ($this->validate) {
            // 验证文件大小
            $contentLength = strlen($Stream);
            if (isset($this->validate['filesize']) && $contentLength > $this->validate['filesize']) {
                throw new UploadException($this->validate_error['filesize']);
            }

            // 验证文件扩展名（从key中提取）
            if (isset($this->validate['fileExt']) && is_array($this->validate['fileExt'])) {
                $extension = pathinfo($key, PATHINFO_EXTENSION);
                if (!in_array(strtolower($extension), $this->validate['fileExt'])) {
                    throw new UploadException($this->validate_error['fileExt']);
                }
            }
        }
    }

    /**
     * 文件流上传
     * @param string $fileContent
     * @param string|null $key
     * @return array|bool|mixed|\StdClass
     */
    public function stream(string $fileContent, string $key = null)
    {
        if (!$key) {
            $key = $this->createFileName();
        }
        $dir = $this->uploadDir($this->path);
        if (!$this->validDir($dir)) {
            throw new UploadException("上传配置有误");
        }
        $fileName = $dir . '/' . $key;
        file_put_contents($fileName, $fileContent);
        $this->fileInfo->uploadInfo = new File($fileName);
        $this->fileInfo->realName = $key;
        $this->fileInfo->fileName = $key;
        $this->fileInfo->filePath = $this->defaultPath . '/' . $this->path . '/' . $key;
        return $this->fileInfo;
    }

    /**
     * 删除文件
     * @param string $filePath
     * @return bool|mixed
     */
    public function delete(string $filePath)
    {
        $_filePath = Filesystem::disk('public')->path($filePath);
        if (file_exists($_filePath)) {
            unlink($_filePath);
            return true;
        }
        return false;
    }

    public function save(string $file = 'file')
    {
        return $this->upload($file);
    }
}
